#ifndef _DICOM_SESSION_MANAGER_H_
#define _DICOM_SESSION_MANAGER_H_

#include <map>
#include <string>
#include <vector>
#include <list>
#include <boost/shared_ptr.hpp>

#include <gwbase/base/mutex.h>
#include <gwbase/base/thread.h>
#include <gwbase/db/sqlite3_wrapper.h>

#include "dicom/dmv_db.h"
#include "dicom/ref_object.h"
#include "dicom/dicom_conf.h"
#include <surbasic/TokenManager.h>

namespace dicom {

struct Credential {
  int64_t surdoc_key;
  std::string token;
  std::string email;
  std::string password;
  std::string ukey_id;
};

typedef boost::shared_ptr<Credential> CredentialPtr;

using namespace SDBasic;
using SDBasic::token::AccessToken;

class ExpiredTokenPool : public tokenmg::TokenExpirationHandler {
 public:
  ExpiredTokenPool() {}
  virtual ~ExpiredTokenPool() {}

  void HandleExpiration(const AccessToken& token);
  bool GetNextPending(AccessToken* token);
  size_t PendingCount() const;

 private:
  mutable dicom::Mutex mu_;
  std::list<AccessToken> pending_;
};

struct SDBasicToken {
  std::string email;
  int64_t surdoc_key;
};

class SessionManager {
 public:
  SessionManager(const DicomConf* conf) : conf_(conf) {};
  virtual ~SessionManager() {}

  std::string Save(const std::string& token, const std::string& surdoc_token,
                   const std::string& email, const std::string& password,
                   const std::string& ukey_id, const std::string& cloud_root_id,
                   const std::string& privkey, int64_t surdoc_key);

  void Logout(const std::string& token);

  CredentialPtr GetUserCredential(const std::string& token);
  bool GetUserCredentialByEmail(const std::string& user, SDBasicToken* out);

  void StartTokenRefresher();

  Status Init();

 private:
  struct InternalCredential {
    std::string surdoc_token;
    std::string email;
    std::string password;
    std::string ukey_id;
    std::string cloud_root_id;
    std::string priv_key;
    int64_t surdoc_key;
    Timestamp update_time;
  };

  typedef boost::shared_ptr<InternalCredential> InternalCredentialPtr;
  typedef std::map<std::string, InternalCredentialPtr> CredentialMap;
  typedef std::map<std::string, Timestamp> TokenExpirationMap;
  typedef std::map<std::string, std::string> UKeyMap;

  void RefreshTokenLoop();
  void RefreshSingleToken(const std::string& name);
  Status OpenTokenDB();
  void LoadSavedTokenFromDB();
  void InsertTokenToDB(const std::string& token,
                       const InternalCredentialPtr& c);
  void DeleteTokenFromDB(const std::string& token);

  db::DbPtr db_;

  Mutex mu_;
  CredentialMap tokens_;
  const DicomConf* conf_;

  ThreadPtr token_refresher_;
  ExpiredTokenPool pool_;
};

extern SessionManager* NewSessionManager(const DicomConf* conf);

extern std::string GenToken();
};

#endif  // _DICOM_SESSION_MANAGER_H_
